﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace FireCol
{
    internal class RouterCompute
    {
        public double[,] CostsMap { get; set; }
        public double[,] CellCosts = new double[200, 200];
        public int Size = 200;
        public Point StartPos { get; set; }=new Point(10,10);
        public Point EndPos { get; set; }= new Point(180,180);
        public double Cost { get;private set; }
        private int currentPostionIndex;
        public int CurrentPostionIndex { get => currentPostionIndex; set => currentPostionIndex = value; }

        public void InitCostsMap()
        {
            CostsMap=new double[200,200];
            for(var  i = 0; i < 200; i++)
            {
                for(var j = 0; j < 200; j++)
                {
                    CostsMap[i, j] = 1;
                }
            }
        }
        public double MaxCellCost
        {
            get
            {
                var maxValue = 0.0;
                for (var i = 0; i < 200; i++)
                {
                    for (var j = 0; j < 200; j++)
                    {
                        maxValue = maxValue > CellCosts[i, j] ? maxValue : CellCosts[i, j];
                    }
                }
                return maxValue;
            }
        }
        public double MaxCostsMapValue
        {
            get
            {
                var maxValue = 0.0;
                for (var i = 0; i < 200; i++)
                {
                    for (var j = 0; j < 200; j++)
                    {
                        if(CostsMap[i, j] < 1e5)
                        {
                            maxValue = maxValue > CostsMap[i, j] ? maxValue : CostsMap[i, j];
                        }
                        
                    }
                }
                return maxValue;
            }
        }


        public void FindRoute()
        {
            // 定义可能的移动方向（上下左右）
            var directions = new DirectionCost[]
            {
                new DirectionCost(1, 1,1.414),
                new DirectionCost(1, -1,1.414),
                new DirectionCost(-1, -1,1.414),
                new DirectionCost(-1, 1,1.414),
                new DirectionCost(-1, 0,1),
                new DirectionCost(1, 0,1),
                new DirectionCost(0, -1,1),
                new DirectionCost(0, 1,1)
            };

            List< Point > cachePathNodes=new List<Point>();
            cachePathNodes.Add(StartPos);
            CellCosts = new double[200, 200];
            for (var i=0; i < 200; i++)
            {
                for(var j=0; j < 200; j++)
                {
                    CellCosts[i, j] = double.MaxValue;
                }
            }
            CellCosts[StartPos.X, StartPos.Y] = 0;

            int settedCount = 0;

            int roundCount = 0;
            while (cachePathNodes.Count>0)
            {
                List<PathNode> newToSetNodes=new List<PathNode>();
                List<Point> newSetNodes=new List<Point>();
                for (var j = 0; j < cachePathNodes.Count; j++)
                {
                    var lastNode = cachePathNodes[j];
                    var lastNodeCost = CellCosts[lastNode.X, lastNode.Y];

                    for (var i = 0; i < directions.Length; i++)
                    {
                        var newPosX = lastNode.X + directions[i].Direction.X;
                        var newPosY = lastNode.Y + directions[i].Direction.Y;
                        if (newPosX >= 0 && newPosY >= 0 && newPosX < Size && newPosY < Size)
                        {
                            var newCost = lastNodeCost + directions[i].Cost * CostsMap[newPosX, newPosY];
                            if (CellCosts[newPosX, newPosY] > newCost)
                            {
                                newToSetNodes.Add(new PathNode(newPosX, newPosY, newCost));
                                newSetNodes.Add(new Point(newPosX, newPosY));
                            }
                        }
                    }
                }
                for (var j =  newToSetNodes.Count-1;j>=0;j--)
                {
                    var pos = newToSetNodes[j];
                    if (CellCosts[pos.Position.X, pos.Position.Y] > pos.Cost)
                    {
                        CellCosts[pos.Position.X, pos.Position.Y] = pos.Cost;
                    }
                }
                cachePathNodes.Clear();
                newSetNodes.Sort(new PointComparer());
                for(var i=newSetNodes.Count-1;i>=1;i--)
                {
                    var pos = newSetNodes[i];
                    var pos1 = newSetNodes[i-1];
                    if(pos.X==pos1.X && pos.Y == pos1.Y)
                    {
                        newSetNodes.RemoveAt(i);
                    }
                }
                settedCount += newSetNodes.Count;
                cachePathNodes.AddRange(newSetNodes);
                roundCount++;
            }
        }


        internal void GoStep(PointPreNode lastPosition)
        {
            var directions = new DirectionCost[]
            {
                new DirectionCost(1, 1,1.414),
                new DirectionCost(1, -1,1.414),
                new DirectionCost(-1, -1,1.414),
                new DirectionCost(-1, 1,1.414),
                new DirectionCost(-1, 0,1),
                new DirectionCost(1, 0,1),
                new DirectionCost(0, -1,1),
                new DirectionCost(0, 1,1)
            };

            var minCostItem = double.MaxValue;
            for (var i = 0; i < directions.Length; i++)
            {
                var x = lastPosition.CurrentPos.X + directions[i].Direction.X;
                var y = lastPosition.CurrentPos.Y + directions[i].Direction.Y;
                if (x >= 0 && y >= 0 && x < Size && y < Size)
                {
                    if (CellCosts[x, y] < minCostItem)
                    {
                        minCostItem = CellCosts[x, y];
                    }
                }
            }
            lastPosition.PrePos = new List<PointPreNode>();
            for (var i = 0; i < directions.Length; i++)
            {
                var x = lastPosition.CurrentPos.X + directions[i].Direction.X;
                var y = lastPosition.CurrentPos.Y + directions[i].Direction.Y;
                if (x >= 0 && y >= 0 && x < Size && y < Size)
                {
                    if (Math.Abs(CellCosts[x, y] - minCostItem) < 1e-3)
                    {
                        var p1 = new PointPreNode { CurrentPos = new Point(x, y) };
                        lastPosition.PrePos.Add(p1);
                    }
                }
            }
            for (var i = 0; i < lastPosition.PrePos.Count; i++)
            {
                var x = lastPosition.PrePos[i].CurrentPos.X;
                var y = lastPosition.PrePos[i].CurrentPos.Y;
                if (x != StartPos.X || y != StartPos.Y)
                {
                    GoStep(lastPosition.PrePos[i]);
                }
            }
        }

        internal void AddFire(int v1, int v2)
        {
            for (var i = -20; i < 20; i++)
            {
                for(var j=-20; j < 20; j++)
                {
                    var x = v1 + i;
                    var y = v2 + j;
                    if (x >= 0 && y >= 0 && x < Size && y < Size)
                    {
                        var len = Math.Sqrt(i * i + j * j);
                        if (len >= 20) { continue; }
                        var costMx = len == 0 ? 2 : 200 / len;
                        CostsMap[x, y] = 1 + costMx;
                    }
                }
            }
        }

        internal void AddWall(Point p1,Point p2)
        {
            var len = Math.Sqrt((p1.X - p2.X) * (p1.X - p2.X) + (p1.Y - p2.Y) * (p1.Y - p2.Y));
            for(var i=1; i < len; i++)
            {
                var pcx = p1.X + (p2.X - p1.X) * i / len;
                var pcy = p1.Y + (p2.Y - p1.Y) * i / len;
                CostsMap[(int)pcx, (int)pcy] = 1e10 ;
                CostsMap[(int)pcx-1, (int)pcy] = 1e10;
                CostsMap[(int)pcx + 1, (int)pcy] = 1e10;
                CostsMap[(int)pcx, (int)pcy - 1] = 1e10;
                CostsMap[(int)pcx, (int)pcy + 1] = 1e10;
            }
            CostsMap[p1.X, p1.Y] = 1e10;
            CostsMap[p2.X, p2.Y] = 1e10;
        }
    }

    class DirectionCost
    {
        public Point Direction;
        public double Cost;

        public DirectionCost(int v1, int v2, double v3)
        {
            this.Direction = new Point(v1, v2);
            this.Cost = v3;
        }
    }
    class PathNode 
    {
        public Point Position { get; set; }
        public double Cost { get; set; }

        public PathNode(int positionX,int posY, double cost)
        {
            Position=new Point(positionX, posY);
            Cost = cost;
        }
        public PathNode(Point position, double cost)
        {
            Position = position;
            Cost = cost;
        }

        public override string ToString()
        {
            return Position.X+" "+ Position.Y+" @"+ Cost; 
        }
    }
    class PointComparer : IComparer<Point>
    {
        int IComparer<Point>.Compare(Point x, Point y)
        {
            if (x.X < y.X) return -1;
            if(x.X > y.X)return 1;
            if(x.Y< y.Y) return -1;
            if(x.Y> y.Y) return 1;
            return 0;
        }
    }

    class PointPreNode
    {
        public Point CurrentPos;
        public List<PointPreNode> PrePos = null;

        internal void Render(Graphics graphics, List<Point> outPoints)
        {
            if (PrePos == null) { return; }
            for (var i = 0; i < PrePos.Count; i++)
            {
                var p = PrePos[i];
                graphics.DrawLine(Pens.Red, new PointF(CurrentPos.X * 2, CurrentPos.Y * 2), new PointF(p.CurrentPos.X * 2, p.CurrentPos.Y * 2));

                outPoints.Add(p.CurrentPos);

                p.Render(graphics, outPoints);
            }
        }
    }
}
